#include <am.h>
#include <klib.h>
#include <klib-macros.h>

#define IP_BASE_ADDR 0x10004000
#define REG_CTRL *((volatile uint32_t *)(IP_BASE_ADDR))
#define REG_PSCR *((volatile uint32_t *)(IP_BASE_ADDR + 4))
#define REG_TXR  *((volatile uint32_t *)(IP_BASE_ADDR + 8))
#define REG_RXR  *((volatile uint32_t *)(IP_BASE_ADDR + 12))
#define REG_CMD  *((volatile uint32_t *)(IP_BASE_ADDR + 16))
#define REG_SR   *((volatile uint32_t *)(IP_BASE_ADDR + 20))

#define I2C_TEST_START       ((uint32_t)0x80)
#define I2C_TEST_STOP        ((uint32_t)0x40)
#define I2C_TEST_READ        ((uint32_t)0x20)
#define I2C_TEST_WRITE       ((uint32_t)0x10)
#define I2C_TEST_START_READ  ((uint32_t)0xA0)
#define I2C_TEST_START_WRITE ((uint32_t)0x90)
#define I2C_TEST_STOP_READ   ((uint32_t)0x60)
#define I2C_TEST_STOP_WRITE  ((uint32_t)0x50)

#define I2C_STATUS_RXACK        (1 << 7)
#define I2C_STATUS_BUSY         (1 << 6)
#define I2C_STATUS_AL           (1 << 5)
#define I2C_STATUS_TIP          (1 << 1)
#define I2C_STATUS_IF           (1 << 0)

uint32_t slv_addr;

uint32_t i2c_get_ack() {
    while ((REG_SR & I2C_STATUS_TIP) == 0); // need TIP go to 1
    while ((REG_SR & I2C_STATUS_TIP) != 0); // and then go back to 0
    return !(REG_SR & I2C_STATUS_RXACK); // invert since signal is active low
}

uint32_t i2c_busy() {
    return ((REG_SR & I2C_STATUS_BUSY) == I2C_STATUS_BUSY);
}

void i2c_setup() {
    REG_TXR = slv_addr;
    REG_CMD = I2C_TEST_START_WRITE;
    if (!i2c_get_ack()) putstr("no ack recv from slv 1\n");

    REG_TXR = (uint32_t)0;
    REG_CMD = I2C_TEST_WRITE;
    if (!i2c_get_ack()) putstr("no ack recv from slv 2\n");

}
int main(){
    putstr("i2c test\n");
    slv_addr = (uint32_t)0xA0;
    REG_CTRL = (uint32_t)0;
    REG_PSCR = (uint32_t)199;
    REG_CTRL = (uint32_t)0x80;

    // write
    i2c_setup();
    for(int i = 0; i < 6; ++i) {
        REG_TXR = i;
        REG_CMD = I2C_TEST_WRITE;
        if (!i2c_get_ack()) putstr("no ack recv from slv 3\n");
    }

    REG_CMD = I2C_TEST_STOP;
    while(i2c_busy());

    // read
    i2c_setup();
    REG_CMD = I2C_TEST_STOP;
    while(i2c_busy());

    REG_TXR = slv_addr + 1;
    REG_CMD = I2C_TEST_START_WRITE;
    if (!i2c_get_ack()) putstr("no ack recv from slv 4\n");

    uint32_t recv_val;
    for (int i = 0; i < 6; ++i) {
        if (i == 5) REG_CMD = I2C_TEST_STOP_READ;
        else REG_CMD = I2C_TEST_READ;

        if (!i2c_get_ack()) putstr("no ack recv from slv 5\n");

        recv_val = REG_RXR;
        if (recv_val != i) putstr("test fail\n");
    }

    putstr("test pass\n");

    return 0;
}
